<?php
declare (strict_types = 1);

namespace app\command\xmake;

use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\helper\Str;
use think\facade\Db;

class Xmake extends Command
{
    protected $tablename = '';

    protected $params = [
        'appname' => '',
        'controllername' => '',
        'controllertitle' => '',
        'modelname' => '',
        'modelnameas' => '', // 实例化别名
        'servicename' => '',
        'viewname' => '',
        'dir' => '',
        'dirname' => '', // 带分隔符的
    ];

    protected $input = null;
    protected $output = null;

    protected function configure()
    {
        // 指令配置
        $this->setName('xmake')
            ->addArgument('table', Argument::REQUIRED, '数据库表名称，小写加下划线方式命名') // 例：shop_goods
            ->addOption('app', 'app', Option::VALUE_OPTIONAL, '应用名称，小写') // 例：shop
            ->addOption('controller', 'controller', Option::VALUE_OPTIONAL, '控制器名称，大驼峰命名规范') // 例：Goods
            ->addOption('title', 'title', Option::VALUE_OPTIONAL, '控制器中文标题') // 例：商品管理
            ->addOption('model', 'model', Option::VALUE_OPTIONAL, '对应模型的名称，大驼峰命名规范') // 例：ShopGoods
            ->addOption('dir', 'dir', Option::VALUE_OPTIONAL, '二级目录名称，小写') // 例：shop
            ->setDescription('自动生成ThinkAdminf风格的控制器');
    }

    protected function execute(Input $input, Output $output)
    {
        $this->input = $input;
        $this->output = $output;

        $this->initParams();

        foreach (['controller', 'model', 'service', 'view.index#html', 'view.index_search#html', 'view.form#html'] as $type) {
            $this->output->writeln("开始创建 {$type}");

            $pathname = $this->getPathName($type);

            if (is_file($pathname)) {
                $this->output->writeln("---- <comment>{$pathname} 文件已存在!</comment>");
                continue;
            }

            if (!is_dir(dirname($pathname))) {

                mkdir(dirname($pathname), 0755, true);
            }

            file_put_contents($pathname, $this->buildClass($type));

            $this->output->writeln("---- <info>创建成功!</info>");
        }
    }
    
    /**
     * 获取文件内容
     *
     * @param string $type
     * @return string
     */
    protected function buildClass(string $type): string
    {
        [$type] = Str::contains($type, '#') ? explode('#', $type) : [$type, null];

        $stub = file_get_contents($this->getStub($type));

        $replacements = [];
        foreach ($this->params as $key => $value) {
            $replacements["{%{$key}%}"] = $value;
        }

        return str_replace(array_keys($replacements), array_values($replacements), $stub);
    }

    /**
     * 获取文件绝对路径
     *
     * @param string $type 文件类型
     * @return string
     */
    protected function getPathName(string $type): string
    {
        [$type, $suffix] = Str::contains($type, '#') ? explode('#', $type) : [$type, null];
        [$type, $filename] = Str::contains($type, '.') ? explode('.', $type) : [$type, null];
        $suffix = is_null($suffix) ? 'php' : $suffix;

        $filename = is_null($filename) ? ($this->params["{$type}name"] ?? '') : $filename;
        
        if (empty($filename)) {
            $this->output->writeln("---- <error>文件名称解析失败!</error>");
            return '';
        }

        $prefix = '';
        if ($type == 'view') {
            $prefix = Str::snake($this->params['controllername']) . DIRECTORY_SEPARATOR;
        }

        $dir = empty($this->params['dir']) ? '' : strtolower($this->params['dir']) . DIRECTORY_SEPARATOR;

        return $this->app->getBasePath() . $this->params['appname'] . DIRECTORY_SEPARATOR . $type . DIRECTORY_SEPARATOR . $dir . $prefix . $filename . '.' . $suffix;
    }

    /**
     * 获取文件模板地址
     *
     * @return string
     */
    protected function getStub($type): string
    {
        $stubPath = __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR;
        return $stubPath . $type . '.stub';
    }

    /**
     * 初始化参数
     *
     * @return boolean
     */
    protected function initParams(): bool
    {
        $this->tablename = Str::lower(trim($this->input->getArgument('table')));
        $this->params['appname'] = trim($this->input->getOption('app')??'');
        $this->params['controllername'] = trim($this->input->getOption('controller')??'');
        $this->params['controllertitle'] = trim($this->input->getOption('title')??'');
        $this->params['modelname'] = trim($this->input->getOption('model')??'');
        $this->params['dir'] = trim($this->input->getOption('dir')??'');

        $name_arr = explode("_", $this->tablename);
        $app = $name_arr[0];
        if (count($name_arr) < 2) {
            if (empty($this->params['appname'])) {
                $this->output->writeln("---- <error>{$this->tablename} 数据库表命名不规范，请输入[app]选项!</error>");
                return false;
            }
            if (empty($this->params['controllername'])) {
                $this->output->writeln("---- <error>{$this->tablename} 数据库表命名不规范，请输入[controller]选项!</error>");
                return false;
            }
            
            $table_abbr = $this->tablename;
        } else {
            $table_abbr = str_replace("{$app}_", '', $this->tablename);
            if (empty($this->params['appname'])) $this->params['appname'] = $app;
            if (empty($this->params['controllername'])) $this->params['controllername'] = Str::studly($table_abbr);
        }

        if (empty($this->params['controllertitle'])) $this->params['controllertitle'] = $this->params['controllername'];
        if (empty($this->params['modelname'])) $this->params['modelname'] = Str::studly($this->tablename);
        if ($this->params['modelname'] == $this->params['controllername']) {
            $this->params['modelnameas'] = "{$this->params['modelname']} AS {$this->params['modelname']}Model";
            $this->params['modelname'] = "{$this->params['modelname']}Model";
        } else {
            $this->params['modelnameas'] = $this->params['modelname'];
        }
        $this->params['servicename'] = Str::studly($table_abbr).'Service';
        $this->params['viewname'] = Str::snake($this->params['controllername']);
        $app_path = $this->app->getBasePath() . $this->params['appname'];
        if (!is_dir($app_path)) {
            mkdir($app_path, 0755, true);
        }
        if (!empty($this->params['dir'])) {
            $this->params['dirname'] = "\\{$this->params['dir']}";
            $this->params['viewname'] = "{$this->params['dir']}/{$this->params['viewname']}";
        }
        return true;
    }
}
